# utils/task_util.py
import os
import logging
import importlib
from threading import Thread
from types import ModuleType
from django.apps import AppConfig
from typing import Any, Self, Type, List, Callable, ClassVar, Optional, cast

from django.apps import apps
from django.dispatch import receiver
from django_apscheduler.jobstores import DjangoJobStore
from django.db.backends.signals import connection_created
from apscheduler.schedulers.background import BackgroundScheduler


# 获取当前模块的日志记录器
logger: logging.Logger = logging.getLogger(__name__)


class TaskManager:
    """集中式日程管理器"""

    _instance: ClassVar[Optional["TaskManager"]] = None
    # 声明实例属性
    scheduler: BackgroundScheduler

    def __new__(cls: Type[Self]) -> Self:
        """创建单例实例"""
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.scheduler = BackgroundScheduler()
            cls._instance.scheduler.add_jobstore(DjangoJobStore(), "default")
            cls._instance._initialized = False
        # 返回时明确断言类型
        return cast(Self, cls._instance)

    def initialize(self: Self) -> None:
        """初始化日程系统"""
        if self._initialized:
            return
        logger.info("⏳ 日程系统开始初始化(0/3)")
        # 检查是否在 ASGI 模式下运行
        is_asgi: bool = "UVICORN_PROCESS" in os.environ
        if is_asgi:  # ASGI 模式下使用线程启动调度器
            logger.info("🔄 检测到 ASGI 模式, 使用线程启动调度器(1/3)")
            thread: Thread = Thread(target=self._start_scheduler, daemon=True)
            thread.start()
        else:  # WSGI 模式下使用信号连接
            connection_created.connect(self._start_scheduler, weak=False)
            logger.info("🛜 已连接数据库信号(1/3)")

    def _autodiscover_tasks(self: Self) -> None:
        """自动发现并注册所有 Django 应用中的任务"""
        logger.info("🔎 开始搜索任务...(2/3)")
        for app_config in apps.get_app_configs():
            self._discover_app_tasks(app_config)

    def _discover_app_tasks(self: Self, app_config: AppConfig) -> None:
        """发现并注册单个应用中的定时任务"""
        app_name: str = app_config.name
        try:
            task_module: ModuleType = importlib.import_module(f"{app_name}.tasks")
            for attr_name in dir(task_module):
                attr: Any = getattr(task_module, attr_name)
                if hasattr(attr, "_scheduler_registration"):
                    logger.info(f"✅ 注册任务: {app_name}.{attr_name}")
                    registration_func: Any = attr._scheduler_registration
                    registration_func()
        except ImportError:
            # 没有 task 模块是正常情况
            pass
        except Exception as e:
            logger.error(f"❌ {app_name} 任务注册失败: {e}")

    def _start_scheduler(self: Self, **kwargs: Any) -> None:
        """启动调度器"""
        if self._initialized:
            return
        try:
            # 确保数据库连接就绪
            from django.db import connection

            connection.ensure_connection()
            if not self.scheduler.running:
                self.scheduler.start()
                self._initialized = True
                # 自动发现任务
                self._autodiscover_tasks()
                mode: str = "ASGI" if "UVICORN_PROCESS" in os.environ else "WSGI"
                logger.info(f"📅 日程管理器以 {mode} 模式启动完成(3/3)")
        except Exception as e:
            logger.error(f"❌ 调度器启动失败: {e}")

    def register_once_task(self: Self, func: Callable) -> Callable:
        """装饰器: 注册在数据库就绪后运行一次的任务"""

        @receiver(connection_created, weak=False)
        def run_on_ready(**kwargs: Any) -> None:
            if not getattr(func, "_has_run", False):
                try:
                    logger.info(f"✅ 执行一次性任务: {func.__name__}")
                    func()
                    setattr(func, "_has_run", True)
                except Exception as e:
                    logger.error(f"❌ 一次性任务失败: {e}")
                finally:
                    connection_created.disconnect(run_on_ready)

        return func

    def register_scheduled_task(self: Self, func: Callable, task_id: str, interval_seconds: float, app_name: str) -> None:
        """注册定时任务到日程管理器"""
        full_task_id: str = f"{app_name}.{task_id}"
        self.scheduler.add_job(func, "interval", seconds=interval_seconds, id=full_task_id, replace_existing=True)
        logger.info(f"✅ 注册定时任务: {full_task_id} (间隔: {interval_seconds}秒)")


# 创建全局日程管理器实例
scheduler_manager = TaskManager()


def once_task(func: Callable) -> Callable:
    """装饰器: 注册在应用启动后运行一次的任务"""
    return scheduler_manager.register_once_task(func)


def scheduled_task(task_id: str, interval_seconds: float) -> Callable:
    """装饰器: 注册定时执行的任务"""

    def decorator(func: Callable) -> Callable:
        # 从函数模块路径推断应用名称
        module_parts: List[str] = func.__module__.split(".")
        app_name: str = module_parts[0] if module_parts else "unknown"

        def register() -> None:
            scheduler_manager.register_scheduled_task(func, task_id, interval_seconds, app_name)

        setattr(func, "_scheduler_registration", register)
        return func

    return decorator
